package cn.kai.basic.service.impl;

import cn.kai.basic.config.BussinessException;
import cn.kai.basic.constant.BaseConstant;
import cn.kai.basic.jwt.JwtUtils;
import cn.kai.basic.jwt.RsaUtils;
import cn.kai.basic.jwt.UserInfo;
import cn.kai.basic.service.ILoginService;
import cn.kai.basic.util.AjaxResult;
import cn.kai.basic.util.HttpUtil;
import cn.kai.basic.util.MD5Utils;
import cn.kai.basic.util.StrUtils;
import cn.kai.basic.vo.LoginAccountVo;
import cn.kai.basic.vo.WeChatVo;
import cn.kai.system.domain.Menu;
import cn.kai.system.mapper.MenuMapper;
import cn.kai.system.mapper.PermissionMapper;
import cn.kai.user.domain.Logininfo;
import cn.kai.user.domain.User;
import cn.kai.user.domain.Wxuser;
import cn.kai.user.mapper.LogininfoMapper;
import cn.kai.user.mapper.UserMapper;
import cn.kai.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.jws.Oneway;
import javax.validation.Valid;
import java.security.PrivateKey;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Value("${jwt.rsa.pri}")
    private String jwtRsaPrivate; // 通过yml配置文件获取私钥

    /**
     * 定义一个生成jwt字符串的方法
     * @param logininfo
     * @return
     */
    public HashMap<String,Object> loginSuccessJwtHandler(Logininfo logininfo){
        //生成一个userInfo对象，方便后面保存logininfo permission和menu
        UserInfo userInfo = new UserInfo();
        userInfo.setLogininfo(logininfo);
        //判断是否是后台登录，即type=0
        if(logininfo.getType() == 0){
            //获取用户的权限信息，这里我们只需要sn即可
            List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
            //获取用户菜单信息
            List<Menu> menus = menuMapper.findByLogininfoId(logininfo.getId());
            //将菜单信息和权限放入userInfo中
//            permissions.stream().forEach(e->{log.info(e);});
            userInfo.setMenus(menus);
            userInfo.setPermissions(permissions);
        }
        //根据密钥文件名称获取到密钥
        PrivateKey privateKey = RsaUtils.getPrivateKey(this.getClass().getClassLoader().getResource(jwtRsaPrivate).getFile());
        //根据密钥，用户信息获取jwt加密字符串
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 600);
        // 按照正常逻辑我们直接返回token给前端即可，但是那样前端需要解密，我们使用简化版本JWT，还是返回用户信息
        // 封装token、用户信息、权限信息、菜单信息给前端
        HashMap<String, Object> map = new HashMap<>();
        map.put("token",token);
        map.put("logininfo",logininfo);
        map.put("permissions",userInfo.getPermissions());
        map.put("menus",userInfo.getMenus());
        return map;
    }


    //账号登录
    @Override
    public Map<String, Object> accountLogin(LoginAccountVo vo) {
        //1.判断非空
        //2.判断用户是否存在
        Logininfo logininfo = logininfoMapper.findByUsername(vo);
        if (vo.getType().equals(0)){ //判断用户为员工或者普通用户
            if(logininfo == null){
                throw new BussinessException("用户名或密码错误");
            }
        }
        //3.判断密码是否正确
        String salt = logininfo.getSalt();
        String password = vo.getPassword();
        String encrypPassword  = MD5Utils.encrypByMd5(password + salt);
        if (StringUtils.isBlank(encrypPassword ) || !encrypPassword .equals(logininfo.getPassword())){
            throw new BussinessException("用户名或密码错误");
        }
        //4.将数据保存到Redis中
//        Map<String, Object> map = GetMap(logininfo);
        //调用JWT串初始化方法，封装map进行返回
        HashMap<String, Object> map = loginSuccessJwtHandler(logininfo); // 改造为使用jwt格式返回
        return map;
    }

    /**
     * 微博登录
     * @param vo
     * @return
     */
    @Override
    public AjaxResult accountWechat(WeChatVo vo) {
        //判断非空
        if(StringUtils.isBlank(vo.getCode())){
            throw new BussinessException("参数为空");
        }
        //通过code调接口取token和openid
        Pair<String, String> tokenAndOpenid = getTokenAndOpenid(vo.getCode()); // 左边是token，右边是openid
        // 通过openid去wxuser表中查询，如果存在就说明之前用wx登录过，就直接登录
        log.info(tokenAndOpenid.getRight());
        Wxuser wxuser = wxuserMapper.findByOpenId(tokenAndOpenid.getRight());
        if(null != wxuser && null != wxuser.getUserId()){
            Logininfo logininfo = logininfoMapper.findByUserId(wxuser.getUserId());
//            Map<String, Object> map = GetMap(logininfo);
            HashMap<String, Object> map = loginSuccessJwtHandler(logininfo); // 改造为使用jwt格式返回
            return AjaxResult.me().success(map);
        }
        //如果不存在就把token和openid返回前端让它去跳转绑定页面
        StringBuffer append = new StringBuffer()
                .append("?accessToken=")
                .append(tokenAndOpenid.getLeft())
                .append("&openId=")
                .append(tokenAndOpenid.getRight());
        return AjaxResult.me().fail().setResultObj(append);
    }

    /**
     * 绑定手机号
     * @param vo
     * @return
     */
    @Override
    public AjaxResult wechatBinder(WeChatVo vo) {
        //校验参数是否为空
        //验证码是否正确
        CheckSmsCode(vo);
        // 调用微信接口 获取用户信息
        String url = String.format(BaseConstant.WeChatCodeCons.WX_LOGIN_USER_INFO,vo.getAccessToken(),
                vo.getOpenId());
        String s = HttpUtil.httpGet(url);
        // json字符串转成 我们的实体
        Wxuser wxuser = JSONObject.parseObject(s, Wxuser.class);
        // 查一下user有没有 没有的话存三张表 有的话存一张表
        User user = userMapper.findByPhone(vo.getPhone());
        Logininfo logininfo = new Logininfo();
        if (null == user){
            // 初始化user对象
            user =  initUser(vo);
            // 初始化logininfo对象
            logininfo =  initLogininfo(user);
            // 保存 logininfo
            logininfoMapper.add(logininfo);
            // 保存user
            user.setLogininfoId(logininfo.getId());
            userMapper.add(user);
        }else {
            // 获取logininfo对象
            logininfo = logininfoMapper.findByUserId(user.getId());
        }
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
        // 保存wxuser
//        Map<String, Object> map = GetMap(logininfo);
        HashMap<String, Object> map = loginSuccessJwtHandler(logininfo); // 改造为使用jwt格式返回
        return AjaxResult.me().success(map);
    }

    private Logininfo initLogininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        logininfo.setType(1);
        return logininfo;
    }

    /**
     * 初始化User
     * @param vo
     * @return
     */
    private User initUser(WeChatVo vo){
        // 盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 密码
        String password = salt + "1";
        String encryPwd = MD5Utils.encrypByMd5(password);
        return User
                .builder()
                .username(vo.getPhone())
                .phone(vo.getPhone())
                .salt(salt)
                .password(encryPwd)
                .state(1)
                .createtime(new Date())
                .build();
    }

    /**
     * 校验验证码是否正确
     * @param vo
     */
    private void CheckSmsCode(WeChatVo vo) {
        String redisSmsCode = (String) redisTemplate.opsForValue()
                .get(String.format(BaseConstant.VerfityCodeCons.BINDER_SMSCODE_PHONE, vo.getPhone()));
        if (redisSmsCode == null){
            throw new BussinessException("验证码错误");
        }
        String[] split = redisSmsCode.split(":");
        String s = split[0];
        if(!s.equals(vo.getVerifyCode())){
            throw new BussinessException("验证码错误");
        }
    }

    /**
     * 传入logininfo对象，返回前端参数
     * @param logininfo
     * @return
     */
    private Map<String, Object> GetMap(Logininfo logininfo) {
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, logininfo,30,TimeUnit.MINUTES);
        // 将logininfo中的敏感字段置空
        logininfo.setPassword("");
        logininfo.setSalt("");
        logininfo.setPhone("");
        Map<String, Object> map = new HashMap<String, Object>(){{
            put("token",token);
            put("logininfo", logininfo);
        }};
        return map;
    }

    /**
     * 通过code调接口取token和openid
     * @return
     */
    private Pair<String,String> getTokenAndOpenid(String code){
        // 根据code调用微信接口获取token和openid
        String tokenURL = String.format(BaseConstant.WeChatCodeCons.WX_LOGIN_TOKEN_URL,
                BaseConstant.WeChatCodeCons.WX_LOGIN_TOKEN_APPID,
                BaseConstant.WeChatCodeCons.WX_LOGIN_TOKEN_SECRET,
                code);
        String s = HttpUtil.httpGet(tokenURL); // 拿到token和openid的JSON字符串
        //转换工具 : 将json字符串转换为json对象
        JSONObject jsonObject = JSONObject.parseObject(s);
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        return Pair.of(access_token,openid);
    }
}
